
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#include <iup.h>
#include <cd.h>
#include <cdiup.h>
#include <im.h>


typedef struct _Image
{
  int width;
  int height;
  int type;

  unsigned char *red;
  unsigned char *green;
  unsigned char *blue;

  unsigned char *map;
  int pal_size;
  long* colors;
} Image;


Image* CreateImage(int width, int height, int type, int pal_size)
{
  int size = width * height;
  Image* image = malloc(sizeof(Image));

  image->width = width;
  image->height = height;
  image->type = type;
  image->pal_size = pal_size;

  if (type == IM_MAP)
  {
    image->colors = (long*)calloc(pal_size, 4);
    image->map = (unsigned char*)calloc(size, 1);

    if (image->map == NULL || image->colors == NULL)
    {
      if (image->map) free(image->map);
      if (image->colors) free(image->colors);
      free(image);
      return NULL;
    }

    image->red = NULL;
    image->green = NULL;
    image->blue = NULL;
  }
  else
  {
    image->red = (unsigned char*)calloc(size, 1);
    image->green = (unsigned char*)calloc(size, 1);
    image->blue = (unsigned char*)calloc(size, 1);

    if (image->red == NULL || image->green == NULL || image->blue == NULL)
    {
      if (image->red) free(image->red);
      if (image->green) free(image->green);
      if (image->blue) free(image->blue);
      free(image);
      return NULL;
    }

    image->map = NULL;
    image->colors = NULL;
  }

  return image;
}


void KillImage(Image* image)
{
  if (image->type == IM_MAP)
  {
    free(image->map);
    free(image->colors);
  }
  else
  {
    free(image->red);
    free(image->green);
    free(image->blue);
  }

  free(image);
}


void ErrorMsg(const char* Func, int err, const char* FileName)
{
  char Text[256];
  strcpy(Text, "File: ");
  strcat(Text, FileName);
  strcat(Text, "\n");
  
  strcpy(Text, Func);
  strcat(Text, " - ");
  
  switch (err)
  {
  case IM_ERR_OPEN:
    strcat(Text, "Error Opening File.\n");
    break;
  case IM_ERR_READ:
    strcat(Text, "Error Reading File.\n");
    break;
  case IM_ERR_WRITE:
    strcat(Text, "Error Writing File.\n");
    break;
  case IM_ERR_TYPE:
    strcat(Text, "Image not Suported.\n");
    break;
  case IM_ERR_FORMAT:
    strcat(Text, "Invalid Format.\n");
    break;
  case IM_ERR_COMP:
    strcat(Text, "Compression not suported.\n");
    break;
  default:
    strcat(Text, "Unknown Error.\n");
  }
  
  IupMessage("Error!", Text);
}


int cbRepaint(Ihandle* iup_canvas)
{
  cdCanvas* cd_canvas = (cdCanvas*)IupGetAttribute(iup_canvas, "cdCanvas");
  Image* image = (Image*)IupGetAttribute(iup_canvas, "Image");

  cdActivate(cd_canvas);
  
  if (image->type == IM_MAP)
  {
    cdPalette(image->pal_size, image->colors, CD_POLITE);
    cdPutImageMap(image->width, image->height, image->map, image->colors, 0, 0, image->width, image->height);
  }
  else
    cdPutImageRGB(image->width, image->height, image->red, image->green, image->blue, 0, 0, image->width, image->height);
  
  cdFlush();
  
  return IUP_DEFAULT;
}
                              

void ShowImage(Image* image, char* FileName)
{
  static Ihandle *iup_dialog = NULL;
  Ihandle *iup_canvas;
  cdCanvas* cd_canvas;
  char Size[20];

  if (iup_dialog)
  {
    cdCanvas* old_cd_canvas = (cdCanvas*)IupGetAttribute(iup_dialog, "cdCanvas");
    Image* old_image = (Image*)IupGetAttribute(iup_dialog, "Image");

    if (old_cd_canvas) cdKillCanvas(old_cd_canvas);
    if (old_image) KillImage(old_image);

    IupDestroy(iup_dialog);
  }

  iup_canvas = IupCanvas("do_nothing");
  IupSetAttribute(iup_canvas, IUP_BUTTON_CB, "cbOpen");
  
  /* Set the Canvas inicial size (IUP will retain this value). */
  sprintf(Size, "%dx%d", image->width, image->height);
  IupSetAttribute(iup_canvas, IUP_RASTERSIZE, Size);
  
  iup_dialog = IupDialog(iup_canvas);
  IupSetAttribute(iup_dialog, IUP_TITLE, FileName); 
  IupSetAttribute(iup_dialog, IUP_CLOSE_CB, "cbClose");

  IupShow(iup_dialog);

  cd_canvas = cdCreateCanvas(CD_IUP, iup_canvas);

  IupSetAttribute(iup_canvas, IUP_ACTION, "cbRepaint");

  IupSetAttribute(iup_canvas, "cdCanvas", (char*)cd_canvas);
  IupSetAttribute(iup_canvas, "Image", (char*)image);
  IupSetAttribute(iup_dialog, "cdCanvas", (char*)cd_canvas);
  IupSetAttribute(iup_dialog, "Image", (char*)image);

  cbRepaint(iup_canvas);
}


int LoadImage(char* FileName)
{
  Image* image;
  int width, height, type, err, pal_size;
  
  err = imImageInfo(FileName, &width, &height, &type, &pal_size);
  if (err)
  {
    ErrorMsg("Image Info", err, FileName);
    return 0;
  }
  
  image = CreateImage(width, height, type, pal_size);
  if (image == NULL)
  {
    IupMessage("Error!", "Insuficient Memory.");
    return 0;
  }

  if (type == IM_RGB)
  {
    /* Reads a RGB Image. */
    err = imLoadRGB(FileName, image->red, image->green, image->blue);
    if (err)
    {
      ErrorMsg("Load RGB", err, FileName);
      KillImage(image);
      return 0;
    }
  }
  else
  {
    /* Reads a Map Image. */
    err = imLoadMap(FileName, image->map, image->colors);
    if (err)
    {
      ErrorMsg("Load Map", err, FileName);
      KillImage(image);
      return 0;
    }
  }
  
  ShowImage(image, FileName);
  
  return 1;
}


int cbOpen(void)
{
  char FileName[200] = "*.*";
  
  if (IupGetFile(FileName) != 0)
    return IUP_DEFAULT;
  
  LoadImage(FileName);
  
  return IUP_DEFAULT;
}


int cbClose(Ihandle* iup_dialog)
{
  cdCanvas* cd_canvas = (cdCanvas*)IupGetAttribute(iup_dialog, "cdCanvas");
  Image* image = (Image*)IupGetAttribute(iup_dialog, "Image");

  if (cd_canvas) cdKillCanvas(cd_canvas);
  if (image) KillImage(image);

  return IUP_CLOSE;
}


char *StrUpper(char *s)
{
  int i, len = strlen(s);
  
  for (i = 0; i < len; i++) 
    s[i] = toupper(s[i]);
  
  return s;
}

int CheckParam(char* Param, const char* Value)
{
  if (Param[0] != '/')
    return 0;
  
  Param++;
  
  if (strstr(StrUpper(Param), Value) == NULL)
    return 0;
  
  return 1;
}


void Usage(void)
{
  IupMessage("Usage:", "ImView <input file>\n"
                       "Display the image of raster formats.\n"
                       "<input file> - Image File Name\n");
}


int main(int NumParam, char* ParamStr[])
{
  char FileName[200] = "*.*";
  
  IupOpen();
  
  /* Try to get a file name from the command line. */
  if (NumParam < 2)
  {
    if (IupGetFile(FileName) != 0)
    {
      IupClose();
      return 1;
    }
  }
  else   
  {
    if (ParamStr[1][0] == '?')
    {
      Usage();
      IupClose();
      return 1;
    }
    
    strcpy(FileName, ParamStr[1]);
  }
                                   
  /* Set IUP callbacks. */
  IupSetFunction("cbRepaint", (Icallback)cbRepaint);
  IupSetFunction("cbOpen", (Icallback)cbOpen);
  IupSetFunction("cbClose", (Icallback)cbClose);
  
  if (!LoadImage(FileName))
  {
    int Try = 1;
    /* If LoadImage returns an error I will try to read another image. */
    /* I can give up on File Open Dialog choosing "Cancel". */
    while (Try)
    {
      strcpy(FileName, "*.*");
      
      if (IupGetFile(FileName) != 0)
      {
        IupClose();
        return 1;
      }
      if (LoadImage(FileName))
        Try = 0;
    }
  }
  
  IupMainLoop();
  
  IupClose();
  
  return 1;
}

